import time, glob, sys
import os
import dbase

class StateMgr:
    AdThreshold = 30
    NsecondsAd = 25
    adFile = "ad1.m3u8"
    DelUser_Threshold = 10
    
    stacklogFileName = "stateMgrlog.txt"
    
    def __init__(self, user = "shafiq"):
        self.db = dbase.dbase(user + ".db")
        self.stacklog = file(StateMgr.stacklogFileName + user, "a")
    
    "Handles all GET requests"
    "switches between regular/ad mode"
    def HandleGET(self, user, name):
        self.stacklog.write("***************************************\n")
        self.stacklog.write("StateMgr:: HandleGET:: Begin\n")
        self.stacklog.write("StateMgr:: HandleGET:: user: " + user + " Name: " + name + "\n" )
        
        #find if the user has timed out
        self.DeleteUser(user)
        
        mode = self.FindMode(user, name)
        self.stacklog.write(user + "will be in mode: " + str(mode) + "\n")
        
        if mode is self.db.REGULAR_MODE:
            self.Get_Regular(user)
        elif mode is self.db.AD_MODE:
            self.Get_Ad(user)
        
        self.stacklog.write("StateMgr:: HandleGET:: End\n")
        self.stacklog.write("***************************************\n\n")
        self.stacklog.close()
        
    "finds what mode the user should be in"
    "updates current state of the user accordingly"
    "pushes/pops the playlist onto stacks for regular/ad mode based on status"    
    def FindMode(self, user, name):
        self.stacklog.write("Finding regular/ad mode for " + user + "\n")
        
        # find if the user is a first timer
        if self.userExists(user) is False:
            self.InitializeFirstTimer(user, name)
            return self.db.REGULAR_MODE
        else:   #user visiting again            
            currentMode = self.GetUserMode(user)
            currentTime = time.time()
            self.stacklog.write("Current Time is : " + str(currentTime) + "\n")
            if currentMode is self.db.REGULAR_MODE:
                if name.find("fileSequence") == -1 and name.find(".ts") != -1:
                    self.Push_Regular(user, name)
                    return self.db.REGULAR_MODE
                lastAccessTime = self.GetLastAccessedTime(user, self.db.REGULAR_MODE)
                if currentTime - lastAccessTime >= self.AdThreshold: #if user has been in regular mode beyond AdThreshold: switch
                    self.SwitchToAdMode(user, name)
                    return self.db.AD_MODE
                else:
                    self.Push_Regular(user, name)
                    return self.db.REGULAR_MODE
            elif currentMode is self.db.AD_MODE:        #if user has been in AD mode beyond N seconds of Ad-play: switch
                self.db.updateHits(user, -2)        #auto-increment
                hitCount = self.db.GetHits(user)
                
                if int(hitCount) > 6:
                    self.SwitchToRegularMode(user, name)
                    return self.db.REGULAR_MODE
                else:
                    self.Push_AdCheat(user, hitCount)
                    return self.db.AD_MODE
                
                """
                lastAccessTime = self.GetLastAccessedTime(user, self.db.AD_MODE)
                if currentTime - lastAccessTime >= self.NsecondsAd:
                    self.SwitchToRegularMode(user, name)
                    return self.db.REGULAR_MODE
                else:
                    self.Push_Ad(user, name)
                    return self.db.AD_MODE
                """
            return currentMode
        
    def InitializeFirstTimer(self, user, name):
        currentTime = time.time()
        self.stacklog.write("First time for " + user + ". Initializing User in db with time: " + str(currentTime) + "\n")
        self.db.insertUserTime(user, currentTime , self.db.REGULAR_MODE)   #insert user with current time stamp for regular mode
        self.db.insertUserTime(user, 0, self.db.AD_MODE)   #insert user with 0 time for Ad mode: never been in ad
        self.SetUserMode(user, self.db.REGULAR_MODE)     #initialize the user mode to regular mode
        
        self.db.updateHits(user, 0)
        # name should be 01.m3u8 or similar
        #latest = self.GetLatestFile(name)   #find latest copy of 01-*.m3u8 from HDD
        # latest found in regular
        
        #push latest.m3u8 on user stack in regular mode
        self.Push_Regular(user, name)
        self.stacklog.write("Initialization complete for " + user + "\n")
    
    def SwitchToAdMode(self, user, name):
        self.stacklog.write("*******Switching to ad-mode for " + user + " *******\n")
        self.SetUserMode(user, self.db.AD_MODE)
        
        # name should be a movie ts file
        # save user state
        # push name on stack in regular mode
        #self.db.Push_Regular(user, name)
        self.Push_Regular(user, name)
        
        #push ad onto ad stack
        adFileName = self.IdentifyAdFile(user, name)
        self.Push_Ad(user, adFileName)
        self.db.updateHits(user, -1)
        
        #set access time for ad to be now
        self.db.insertUserTime(user, time.time(), self.db.AD_MODE)
        self.stacklog.write("******* Switching to ad-mode for " + user + " completed *******\n")
    
    def SwitchToRegularMode(self, user, name):
        self.stacklog.write("******* Switching to regular-mode for " + user + " *******\n")
        self.db.insertUserTime(user, time.time(), self.db.REGULAR_MODE)   #insert user with current time stamp for regular mode
        self.SetUserMode(user, self.db.REGULAR_MODE)
        
        # name should be an ad ts file
        # pop/flush the ad file from ad stack
        self.Clear_Ad(user)
        self.stacklog.write("******* Switching to regular-mode for " + user + " completed *******\n")
        
    def Get_Regular(self, user):
        self.stacklog.write("GET Regular Begin for " + user + ": \n")
        
        #retrieve from user regular stack
        fileName = self.Pop_Regular(user)
        
        self.stacklog.write("Trying to write: " + fileName + " to output stream \n")
        # if file does not exist, try and see if file with next sequence can be retrieved
        if not os.path.exists(fileName):
            self.stacklog.write("Failed to write: " + fileName + " to output stream (file not found) \n")
            sys.stderr.write("Failed to write: " + fileName + " to output stream (file not found) \n")
            splitName = fileName.split("-")
            if fileName.endswith(".ts") is True:
                seq = splitName[2]
                temp = seq.split(".")
                seq = temp[0]
                seq = int(seq) + 1
                
                while True:
                    newFile = splitName[0] + '-' + splitName[1] + '-' + str(seq) + ".ts"
                    if os.path.exists(newFile):
                        fileName = newFile
                        break
                    seq = int(seq) + 1
            elif fileName.find(".m3u8") != -1:
                channel = splitName[0]  #01.m3u8
                while True:
                    newFile = self.GetLatestFile(channel)
                    if os.path.exists(newFile):
                        fileName = newFile
                        break
                
            #find next sequence file which can be written
        
        self.stacklog.write("Writing: " + fileName + " to output stream \n")
        sys.stdout.write("Content-Length: %d\r\nContent-Type: %s\r\n\r\n" % (os.path.getsize(fileName), self.GetContentType(fileName)))
        self.writeFileToStdout(fileName)
        self.stacklog.write("GET Regular End for " + user + "\n")
    
    def Get_Ad(self, user):
        self.stacklog.write("GET Ad Begin for " + user + "\n")
        # retrieve from ad stack
        fileName = self.Pop_Ad(user)
        
        # HACK: When nothing is returned push bip-bop again
#        if fileName is None:
#            self.stacklog.write("No ad file found. Pushing bipbop again\n")
#            self.db.insertUserTime(user, time.time(), self.db.AD_MODE)
#            self.Push_Ad(user, self.adFile)
#            fileName = self.adFile
        
        self.stacklog.write("Writing: " + fileName + " to output stream \n")
        sys.stdout.write("Content-Length: %d\r\nContent-Type: %s\r\n\r\n" % (os.path.getsize(fileName), self.GetContentType(fileName)))
        self.writeFileToStdout(fileName)
        self.stacklog.write("GET Ad End for " + user + "\n")
    
    def Push_Regular(self, user, name):
        #if file is a m3u8 file: find the latest
        if name.find(".m3u8") != -1:    #and name.find(StateMgr.adFile) == -1
            latest = self.GetLatestFile(name)
            name = latest
        
        self.stacklog.write("Pushing " + name + " for " + user + " on regular stack in database\n")
        self.db.push_Regular(user, name)
        self.stacklog.write("Pushing " + name + " completed for " + user + "\n")

    def Pop_Regular(self, user):
        fileName = self.db.pop_Regular(user)
        self.stacklog.write("Popped file for " + user + " is: " + fileName + " in regular mode\n")
        return fileName
    
    def Pop_Ad(self, user):
        fileName = self.db.pop_Ad(user)
        if fileName is not None:
            self.stacklog.write("Popped file for " + user + " is: " + fileName + " in Ad mode\n")
        return fileName
            
    def Push_Ad(self,user, name):
        #HACK: always pushing adfiles when in ad mode
#        if name.find(".m3u8") != -1 and name.find("ad") == -1:
#            adName = self.IdentifyAdFile(user, name)
#            self.stacklog.write("Tried to write non-ad m3u8 file onto ad stack: " + name + "\n")
#            self.stacklog.write("Instead Pushing " + adName + " to ad stack \n")
#            name = adName
#            #self.db.insertUserTime(user, time.time(), self.db.AD_MODE)
#        elif name.find(".ts") != -1 and name.find("fileSequence") == -1:
#            adName = self.IdentifyAdFile(user, name)
#            self.stacklog.write("Tried to write non-ad file TS onto ad stack: " + name + " \n")
#            self.stacklog.write("Instead Pushing " + adName + " to ad stack \n")
#            name = adName #"YellowPages.ts"
            #self.db.insertUserTime(user, time.time(), self.db.AD_MODE)
        self.stacklog.write("Pushing " + name + " on " + user + " Ad stack in database at roughly: " + str(time.time()) + " \n")
        self.db.push_Ad(user, name)
        self.stacklog.write("Pushing " + name + " completed for " + user + "\n")
    
    def GetLastAccessedTime(self, user, mode):
        self.stacklog.write("Retrieving Last access time for " + user + " in " + str(mode) + " mode\n")
        t = self.db.getLastAccessedTime(user, mode)
        self.stacklog.write("Last Accessed Time: " + str(t) + "\n")
        self.stacklog.write("Retrieving complete for " + user + " in " + str(mode) + " mode\n")
        return float(t)
    
    def userExists(self, user):
        exists = self.db.userExists(user)
        self.stacklog.write(user + " existence: " + str(exists) + "\n")
        return exists

    def GetUserMode(self, user):
        mode = self.db.getUserMode(user)
        self.stacklog.write("Get User Mode: " + user + " : " + mode + "\n")
        return int(mode)
    
    def SetUserMode(self, user, mode):
        self.db.setUserMode(user, mode)
        self.stacklog.write("Set User Mode to: " + user + " : " + str(mode) + "\n")  
    
    def Clear_Ad(self, user):
        self.db.clearAdStack(user)
        self.stacklog.write("Clearing ad stack for " + user + "\n")
        
    def GetLatestFile(self, name):
        self.stacklog.write("Retrieving latest file for " + name + "\n")
        
        names = glob.glob("%s-*"%name)
        if len(names) == 0:
            self.stacklog.write("No file found for " + name)
            sys.stderr.write("No file found for " + name)
            sys.stdout.write("Status: 404 Not Found\r\nContent-Length: 0\r\nContent-Type: text/plain\r\n\r\n")
        else:
            (latest, filename) = max((self.GetModTime(filename), filename) for filename in names)
            return filename
    
    def GetContentType(self, name):    
        contentType = ''
        if name.find(".m3u8") is not -1:
            contentType = "application/vnd.apple.mpegurl"
        elif name.endswith(".ts"):
            contentType = "video/mpeg"
        elif name.endswith(".jpg"):
            contentType = "image/jpeg"
        elif name.endswith(".key"):
            contentType = "application/binary"
        return contentType

    def writeFileToStdout(self, name):
        f = open(name, "rb")
        while True:
            next = f.read(4096)
            if not next:
                break
            sys.stdout.write(next)
        f.close()
        
    def GetModTime(self, name):
        try:
            return os.path.getmtime(name)
        except:
            return 0
    
    def DeleteUser(self, user):
        if self.userExists(user) is False:      # find if first time user
            return
        else:
            currentTime = time.time()
            lastPacketTime = self.db.GetLastRequestTime(user)
            self.stacklog.write("Last Packet time: " + str(lastPacketTime) + " and currentTime: " + str(currentTime) + " \n")
            if lastPacketTime == 0:
                self.db.UpdateRequestTime(user, currentTime)
                return None
            elif currentTime - lastPacketTime > self.DelUser_Threshold:
                self.stacklog.write("Current access time:  " + str(currentTime) + " \n")
                self.stacklog.write("User last accessed at " + str(lastPacketTime)+ " \n")
                self.stacklog.write("User timed out in DB. Deleting " + user + " from db !!!!!!!!!!!!!! \n\n")
                self.db.DeleteUser(user)
            else:
                self.db.UpdateRequestTime(user, currentTime)
                
            return
        
    def IdentifyAdFile(self, user, name):
        self.stacklog.write("Identify ad file for: " + name + " \n")
        channel = "01"
        adFile = ""
        fileName = name.lstrip('./')
        splitName = fileName.split("-")
        if fileName.endswith("ts"):
            #channel = splitName[1]
            seq = "fileSequence"
            if user is "shafiq":
                adFile = "fileSequence"
            else:
                adFile = "fileSequences"
            adFile = seq + "-" + channel + "-0.ts"
#            adFile = "ad-" + channel + ".m3u8"   #ad-01.m3u8
        elif fileName.find(".m3u8") != -1:
            #channel = splitName[0]      #01.m3u8
            #adFile = "ad-" + channel + ".m3u8"
            #adFile = self.GetLatestFile(name)
            adFile = "ad-01.m3u8"
            if user.find("shafiq") != -1:
                adFile = "ad-01.m3u8"
            else:
                adFile = "ads-01.m3u8"
        self.stacklog.write("Corresponding Ad file: " + name + " is: " + adFile + " \n")
        return adFile
    
    def Push_AdCheat(self, user, hitCount):
        name =  "fileSequence-" + "01" + "-" + hitCount + ".ts"
        if user.find("shafiq") != -1:
            name =  "fileSequence-" + "01" + "-" + hitCount + ".ts"
        elif user.find("enrique") != -1:
            name =  "fileSequences-" + "01" + "-" + hitCount + ".ts"
        self.stacklog.write("Pushing " + name + " on " + user + " Ad stack in database at roughly: " + str(time.time()) + " \n")
        self.db.push_Ad(user, name)
        self.stacklog.write("Pushing " + name + " completed for " + user + "\n")